<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>02-画2D正方形</title>
</head>
<body onload="main()">
  <canvas id="glcanvas" width="640" height="480" style="background-color: red;"></canvas>
</body>
<!-- <script src="../glMatrix-0.9.6.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.8.1/gl-matrix-min.js"
  integrity="sha512-zhHQR0/H5SEBL3Wn6yYSaTTZej12z0hVZKOv3TwCUXT1z5qeqGcXJLLrbERYRScEDDpYIJhPC1fk31gqR783iQ=="
  crossorigin="anonymous">
</script>
<script>
  /**
   * GLSL 数据类型 attributes, varyings, uniforms
   * attributes: 可以被 js 代码操作，也可以在 vertex shader 中被作为变量访问。通常被用于存储颜色、纹理坐标以及其他需要在 js 代码和 vertex shader 之间互相传递的数据。
   * varyings: 用于从 vertex shader 向 fragment shader 传递数据。
   * uniforms: 通常是由 js 代码设置并且在 vertex shader 和 fragment shader 中都能够访问。 使用 uniform 设定在一帧的所有绘制中相同的数据，例如光源颜色、亮度、全局变换以及透视数据等等。
  */
// 顶点着色器
const vsSource = `
  attribute vec4 aVertexPosition;

  uniform mat4 uModelViewMatrix;
  uniform mat4 uProjectionMatrix;

  void main() {
    gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
  }
` 
// 片断着色器
const fsSource = `
  void main() {
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
  }
`
function main() {
  const canvas = document.getElementById('glcanvas')
  // 初始化 WebGL 上下文
  const gl = canvas.getContext('webgl')

  if(!gl) {
    alert('无法初始化 WebGL 上下文，您的浏览器、操作系统或硬件等可能不支持 WebGL。')
    return null
  }
  const shaderProgram = initShaderProgram(gl, vsSource, fsSource)

  const programInfo = {
    program: shaderProgram,
    attribLocations: {
      vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
    },
    uniformLocations: {
      projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
      modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix')
    }
  }

  const buffers = initBuffers(gl)
  drawScene(gl, programInfo, buffers)
}

// 初始化着色器程序
function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
  // 创建着色器程序
  const shaderProgram = gl.createProgram(); // 创建并初始化一个 WebGLProgram 对象
  gl.attachShader(shaderProgram, vertexShader); // 将 VERTEX_SHADER 附加在 shaderProgram 上
  gl.attachShader(shaderProgram, fragmentShader); // 将 FRAGMENT_SHADER 附加在 shaderProgram 上
  gl.linkProgram(shaderProgram); // 链接给定的 WebGLProgram 对象，从而完成为程序的片元和顶点着色器准备 GPU 代码的过程。

  // getProgramParameter 返回 WebGLProgram 的信息。参数1: 信息来源的 WebGLProgram；参数2: 查看的信息类型，参考MDN。
  // getProgramInfoLog 返回给定的 WebGLProgram 的信息记录。包括 链接错误 及 校验错误。
  if(!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
    alert('Unable to  initialize the shader program' + gl.getProgramInfoLog(shaderProgram))
    return null
  }
  return shaderProgram
}
// 创建指定类型的着色器，上传 source 源码并编译
function loadShader(gl, type, source) {
  // 创建新的着色器 create a WebGLShader that then be configured further using shaderSource() and compileShader()
  const shader = gl.createShader(type)

  // Send the source to the shader project 将着色器源码发送到 shader 上
  gl.shaderSource(shader, source)

  // Compile the shader program 编译着色器  compile a GLSL shader into binary data so that it can be used by a WebGLProgram.
  gl.compileShader(shader)

  // See if it compile successfully 检查编译是否成功  getShaderParameter: returns information about the given shader. params2: infoType-- a specifying the information to query.
  // getShaderInfoLog: returns the information log for the specified WebGLShader object. It contains warnings, debugging and compile information.
  if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
    alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader))
    gl.deleteShader(shader)
    return null
  }
  return shader
}

// 创建顶点缓冲区
function initBuffers(gl) {
  // createBuffer: creates and initializes a WebGLBuffer storing data such as vertices or colors.
  const positionBuffer = gl.createBuffer()  // 创建缓冲区对象并存储在顶点缓冲器
  // binds a given WebGLBuffer to a target. params1: a specifying the binding point(target). params2: a WebGLBuffer to bind.
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer)  // 绑定上下文 
  
  // 正方体的每个顶点
  var vertices = [
    1.0, 1.0,
    -1.0, 1.0,
    1.0, -1.0,
    -1.0, -1.0,
  ]
  // 将顶点数组转换为浮点类型数组，并建立对象的顶点
  // initializes and creates the buffer object's data store.
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW)
  return {
    position: positionBuffer
  }
}
// 绘制场景
function drawScene(gl, programInfo, buffers) {
  // 使用完全不透明的黑色清除所有图像
  gl.clearColor(0.0, 0.0, 0.0, 1.0)
  gl.clearDepth(1.0)
  gl.enable(gl.DEPTH_TEST)
  gl.depthFunc(gl.LEQUAL)
  // 用上面指定的颜色清除缓冲区
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

  const fieldOfView = 45 * Math.PI / 180
  const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight
  const zNear = 0.1
  const zFar = 100.0
  const projectionMatrix = mat4.create()

  mat4.perspective(projectionMatrix,
                    fieldOfView,
                    aspect,
                    zNear,
                    zFar)
  const modelViewMatrix = mat4.create()
  mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0])

  {
    const numComponents = 2
    const type = gl.FLOAT
    const normalize = false
    const stride = 0
    const offset = 0
    gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position)
    gl.vertexAttribPointer(
      programInfo.attribLocations.vertexPosition,
      numComponents,
      type,
      normalize,
      stride,
      offset
    )
    gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition)
  }

  gl.useProgram(programInfo.program)
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.projectionMatrix,
    false,
    projectionMatrix
  )
  gl.uniformMatrix4fv(
    programInfo.uniformLocations.modelViewMatrix,
    false,
    modelViewMatrix
  )

  {
    const offset = 0
    const vertexCount = 4
    gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount)
  }
}

</script>
</html>